/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/* 
 * Copyright (c) 2009 City University of Hong Kong
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License m_version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Author: Wilson Thong (wilsonwk@ee.cityu.edu.hk) 
 */
#include "traffic-alloc-helper.h"
#include "unicast-session-helper.h"
#include "unicast-session-monitor.h"
#include "ns3/log.h"

NS_LOG_COMPONENT_DEFINE ("TrafficAllocHelper");

namespace ns3 {
namespace eecityu {

using namespace std;

TrafficAllocHelper::TrafficAllocHelper(void)
{
}

TrafficAllocHelper::~TrafficAllocHelper(void)
{
  NS_LOG_FUNCTION_NOARGS ();

  m_createSessionsHelper = 0;
  m_monitors.clear ();
  m_applications.clear ();
}

ApplicationContainer
TrafficAllocHelper::RandomAllocate (BaseTopology& net, Ptr<UnicastSessionHelper> helper, uint32_t nSessions)
{
  NS_LOG_FUNCTION (&net << helper << nSessions);
  
  // clear all old monitors and old app
  m_monitors.clear ();
  m_applications.clear ();
  m_createSessionsHelper = helper;
  // To cater the generated app
  ApplicationContainer app;

  // get the number of nodes and prepare for choosing nodes uniformly at random
  uint32_t nNodes = net.GetNNodes ();
  NS_ASSERT_MSG (nNodes == NodeList::GetNNodes (), "Not all nodes are in the given topology!");
  // create number of unicast sessions as indicated by 'offers' parameter
  while (nSessions > 0) 
  {
    nSessions--;
    // choose two different nodes uniformly at random
    uint32_t srcId = m_rand.GetInteger (0, nNodes-1);
    // the following while loop is used to prevent choosing the same node for both src and dst
    uint32_t dstId = srcId;
    while (dstId == srcId) dstId = m_rand.GetInteger (0, nNodes-1);
    Ptr<Node> srcNode = NodeList::GetNode (srcId);
    Ptr<Node> dstNode = NodeList::GetNode (dstId);
    // Use a helper to install unicast session on a node pair.
    // Assign different UDP port to each unicast session.
    // Add the app created to the container for later returning them back to the caller
    app.Add (helper->InstallSession (srcNode, dstNode));
    // print a logic log
    UintegerValue remotePortVal;
    helper->GetLastAllocatedSourceApplication ()->GetAttribute ("RemotePort", remotePortVal);
    NS_LOG_LOGIC ("Session = " << m_monitors.back ()->GetSessionIndex () 
      << ", source node = " << srcId 
      << ", destination node = " << dstId
      << ", destination port = " << remotePortVal.Get ());
  }
  // Copy the generated app into the local storage for generating unicast session monitors, if requested
  m_applications.insert (m_applications.begin (), app.Begin (), app.End ());
  return app;
}

list< Ptr<UnicastSessionMonitor> > 
TrafficAllocHelper::GetMonitors ()
{
  NS_LOG_FUNCTION_NOARGS ();
  NS_ASSERT_MSG (m_applications.size () % 2 == 0, 
    "It is unexpected to have only odd number of applications generated!");
  NS_ASSERT_MSG (m_createSessionsHelper, 
    "No monitors can be generated without allocating a new set of unicast sessions!");

  ApplicationList_t::const_iterator sourceAppIter = m_applications.begin ();
  ApplicationList_t::const_iterator sinkAppIter = m_applications.begin ();
  sinkAppIter++;

  while (sourceAppIter != m_applications.end ())
  {
    // create a monitor to gather traffic statistics, and add it into the 
    // list for returning all m_monitors to the caller
    m_monitors.push_back (m_createSessionsHelper->CreateMonitor (*sourceAppIter, *sinkAppIter));
    // skip to the next source application
    sourceAppIter++; sourceAppIter++;
    // skip to the next sink application
    sinkAppIter++; sinkAppIter++;
    NS_LOG_LOGIC ("Generated a monitor for the session from node-" 
      << (*sourceAppIter)->GetNode ()->GetId () << " to node-"
      << (*sinkAppIter)->GetNode ()->GetId ());
  }

  m_createSessionsHelper = 0;
  return m_monitors;
}

} // namespace eecityu
} // namespace ns3
